package com.newlink.building.common_base.utils

import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.os.Process
import android.util.Log
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.Date


/**
 * Created by xiaodong on 2024/12/27.
 */
class Base_CrashHandler private constructor(private val mContext: Context) :
    Thread.UncaughtExceptionHandler {
    private var mDefaultCrashHandler: Thread.UncaughtExceptionHandler? = null

    fun init() {
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler()
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    /**
     * 这个是最关键的函数，当程序中有未被捕获的异常，系统将会自动调用#uncaught-
     * thread为出现未捕获异常的线程，ex为未捕获的异常，有了这个ex，就可以得到异常信息
     */
    override fun uncaughtException(thread: Thread?, ex: Throwable) {
        try {
            //导入异常信息到外部存储
            dumpExceptionToExternalStorage(ex)
            //这里可以上传异常信息到服务器，便于开发人员分析日志从而解决bug
            uploadExceptionToServer()
        } catch (e: IOException) {
            e.printStackTrace()
        }

        ex.printStackTrace()
        //如果系统提供了默认的异常处理器，则交给系统去结束程序，否则就由自己结束自己
        if (mDefaultCrashHandler != null) {
            mDefaultCrashHandler!!.uncaughtException(thread, ex)
        } else {
            Process.killProcess(Process.myPid())
        }
    }

    @Throws(IOException::class)
    private fun dumpExceptionToExternalStorage(ex: Throwable) {
        //如果SD卡不存在或无法使用，则无法把异常信息写入SD卡
        if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
            if (DEBUG) {
                Log.w(TAG, "sdcard unmounted,skip dump exception")
                return
            }
        }

        val current = System.currentTimeMillis()
        val time: String = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(Date(current))
        val externalDir: File? = mContext.getExternalFilesDir(null)
        val logFile = File(externalDir, FILE_NAME + time + FILE_NAME_SUFFIX)

        try {
            val pw = PrintWriter(BufferedWriter(FileWriter(logFile)))
            pw.println(time)
            dumpPhoneInfo(pw)
            pw.println()
            ex.printStackTrace(pw)
            pw.close()
        } catch (e: Exception) {
            Log.e(TAG, "dump crash info failing")
        }
    }

    @Throws(PackageManager.NameNotFoundException::class)
    private fun dumpPhoneInfo(pw: PrintWriter) {
        val pm: PackageManager = mContext.packageManager
        val pi: PackageInfo =
            pm.getPackageInfo(mContext.packageName, PackageManager.GET_ACTIVITIES)
        pw.print("APP Version: ")
        pw.print(pi.versionName)
        pw.print('_')
        pw.println(pi.versionCode)

        //Android版本号
        pw.print("OS Version: ")
        pw.print(Build.VERSION.RELEASE)
        pw.print('_')
        pw.println(Build.VERSION.SDK_INT)

        //手机制造商
        pw.print("Vendor: ")
        pw.println(Build.MANUFACTURER)

        //手机型号
        pw.print("Model: ")
        pw.println(Build.MODEL)

        //CPU架构
        pw.print("CPU ABI: ")
        pw.println(Build.CPU_ABI)
    }

    private fun uploadExceptionToServer() {
        //TODO Upload Exception Message To Web Server
    }

    companion object {
        const val TAG = "CrashHandler"
        const val DEBUG = true

        const val FILE_NAME = "crash"
        const val FILE_NAME_SUFFIX = ".trace"

        private var instance: Base_CrashHandler? = null
        fun getInstance(context: Context): Base_CrashHandler? {
            if (instance == null) {
                instance = Base_CrashHandler(context)
            }
            return instance
        }
    }
}